//
//  _SW__Weighted_Grid_RendererPlugIn.m
//  [SW] Weighted Grid Renderer
//
//  Created by David Langford on 22/09/09.
//  Copyright (c) 2009 Sentient Worlds. All rights reserved.
//

/* It's highly recommended to use CGL macros instead of changing the current context for plug-ins that perform OpenGL rendering */
#import <OpenGL/CGLMacro.h>

#import "_SW__Weighted_Grid_RendererPlugIn.h"

#define	kQCPlugIn_Name				@"[SW] Weighted Grid Renderer"
#define	kQCPlugIn_Description		@"Will render the weighted grid array from [SW] Weighted Grid Generator.\n\n\nCreated by David Langford, feel free to email me at langford.d@gmail.com if you have any questions or if you want me to add functionality for you. You are also free to modify the source code."

@implementation _SW__Weighted_Grid_RendererPlugIn

@dynamic 
		  inputGridOutlineColour
		, inputCellColor
		, inputCellPaddingPercent
		, inputWeightedGridArray
		, inputGridOutlineUsesWeight
		, inputShowGridOutlines;

+ (NSDictionary*) attributes
{
	/*
	Return a dictionary of attributes describing the plug-in (QCPlugInAttributeNameKey, QCPlugInAttributeDescriptionKey...).
	*/
	
	
	return [NSDictionary dictionaryWithObjectsAndKeys:kQCPlugIn_Name, QCPlugInAttributeNameKey, kQCPlugIn_Description, QCPlugInAttributeDescriptionKey, nil];
}

+ (NSDictionary*) attributesForPropertyPortWithKey:(NSString*)key
{
	if([key isEqualToString:@"inputWeightedGridArray"])
		return [NSDictionary dictionaryWithObjectsAndKeys:@"Weighted Grid Array", QCPortAttributeNameKey, nil];
	
	if([key isEqualToString:@"inputColor"])
        return [NSDictionary dictionaryWithObjectsAndKeys:@"Cell Colour", QCPortAttributeNameKey, nil];
	
	if ([key isEqualToString:@"inputGridOutlineColour"])
		return [NSDictionary dictionaryWithObjectsAndKeys:@"Grid Outline Colour", QCPortAttributeNameKey, nil];

	if ([key isEqualToString:@"inputShowGridOutlines"])
		return [NSDictionary dictionaryWithObjectsAndKeys:@"Show Grid Outline", QCPortAttributeNameKey, nil];
	
	if ([key isEqualToString:@"inputGridOutlineUsesWeight"])
		return [NSDictionary dictionaryWithObjectsAndKeys:@"Grid Outline uses weight", QCPortAttributeNameKey, nil];
	if ([key isEqualToString:@"inputCellColor"])
		return [NSDictionary dictionaryWithObjectsAndKeys:@"Cell Colour", QCPortAttributeNameKey, nil];
	
	if ([key isEqualToString:@"inputCellPaddingPercent"])
		return [NSDictionary dictionaryWithObjectsAndKeys:@"Cell Padding", QCPortAttributeNameKey
				, [NSNumber numberWithFloat:0.5], QCPortAttributeMinimumValueKey
				, [NSNumber numberWithFloat:1.0], QCPortAttributeMaximumValueKey
				, [NSNumber numberWithFloat:0.75], QCPortAttributeDefaultValueKey
				, nil];
	
	return nil;
}

+ (QCPlugInExecutionMode) executionMode
{
	/*
	Return the execution mode of the plug-in: kQCPlugInExecutionModeProvider, kQCPlugInExecutionModeProcessor, or kQCPlugInExecutionModeConsumer.
	*/
	
	return kQCPlugInExecutionModeConsumer;
}

+ (QCPlugInTimeMode) timeMode
{
	/*
	Return the time dependency mode of the plug-in: kQCPlugInTimeModeNone, kQCPlugInTimeModeIdle or kQCPlugInTimeModeTimeBase.
	*/
	
	return kQCPlugInTimeModeNone;
}

- (id) init
{
	if(self = [super init]) {
		/*
		Allocate any permanent resource required by the plug-in.
		*/
	}
	
	return self;
}

- (void) finalize
{
	/*
	Release any non garbage collected resources created in -init.
	*/
	
	[super finalize];
}

- (void) dealloc
{
	/*
	Release any resources created in -init.
	*/
	
	[super dealloc];
}

@end

@implementation _SW__Weighted_Grid_RendererPlugIn (Execution)

- (BOOL) startExecution:(id<QCPlugInContext>)context
{
	/*
	Called by Quartz Composer when rendering of the composition starts: perform any required setup for the plug-in.
	Return NO in case of fatal failure (this will prevent rendering of the composition to start).
	*/
	
	return YES;
}

- (void) enableExecution:(id<QCPlugInContext>)context
{
	/*
	Called by Quartz Composer when the plug-in instance starts being used by Quartz Composer.
	*/
}

- (BOOL) execute:(id<QCPlugInContext>)context atTime:(NSTimeInterval)time withArguments:(NSDictionary*)arguments
{
	
	// Define a context and set it. This line causes OpenGL to use macros.
	CGLContextObj		cgl_ctx = [context CGLContextObj];
	GLint				saveMode;
	const CGFloat*		cellColorComponents;
	const CGFloat*		gridOutlineColorComponents;
	GLenum				error;
	
	if(cgl_ctx == NULL)
		return NO;
	
	// Save and set the modelview matrix
	glGetIntegerv(GL_MATRIX_MODE, &saveMode);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	
	// Get the colour components for the cells and the grid outline
	cellColorComponents = CGColorGetComponents(self.inputCellColor);
	gridOutlineColorComponents = CGColorGetComponents(self.inputGridOutlineColour);
	
	// Enable transparency
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glEnable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);

	// Render the grid
	int xIndex = 0;
	int yIndex = 1;
	int widthIndex = 2;
	int heightIndex = 3;
	int weightIndex = 4;
	for(int rowIndex=0; rowIndex<[self.inputWeightedGridArray count]; rowIndex++)
	{
		NSArray* row = [self.inputWeightedGridArray objectAtIndex: rowIndex];
		for(int columnIndex=0; columnIndex<[row count]; columnIndex++)
		{
			
			// Get the cell's details
			NSArray* cell = [row objectAtIndex:columnIndex];
			double x = [[cell objectAtIndex:xIndex] doubleValue];
			double y = [[cell objectAtIndex:yIndex] doubleValue];
			double width = [[cell objectAtIndex:widthIndex] doubleValue];
			double height = [[cell objectAtIndex:heightIndex] doubleValue];
			double weight = [[cell objectAtIndex:weightIndex] doubleValue];
			
			// Find the dimensions
			double halfWidth = width/2;
			double halfHeight = height/2;
			
			// Save state
			glPushMatrix();
			
			// Move to where the cell should be
			glTranslatef(x, y, 0.0);			
			
			// Set colour based on state
			glColor4f(cellColorComponents[0],cellColorComponents[1],cellColorComponents[2], weight);
			
			// Get the padding info
			double widthPadding = width*self.inputCellPaddingPercent;
			double heightPadding = height*self.inputCellPaddingPercent;
			
			// Render the quad
			glBegin(GL_QUADS);
			glVertex3f(halfWidth-widthPadding, halfHeight-heightPadding, 0); // upper right
			glVertex3f(-halfWidth+widthPadding, halfHeight-heightPadding, 0); // upper left
			glVertex3f(-halfWidth+widthPadding, -halfHeight+heightPadding, 0); // lower left
			glVertex3f(halfWidth-widthPadding, -halfHeight+heightPadding, 0); // lower right
			glEnd();
			
			
			// Render its outline
			if (self.inputShowGridOutlines)
			{
				// Determine alpha
				float alpha;
				if (self.inputGridOutlineUsesWeight)
					alpha = weight;
				else 
					alpha = gridOutlineColorComponents[3];

				glColor4f(gridOutlineColorComponents[0],gridOutlineColorComponents[1],gridOutlineColorComponents[2], alpha);
				glBegin(GL_LINE_LOOP);
				glVertex3f(halfWidth, halfHeight, 0); // upper right
				glVertex3f(-halfWidth, halfHeight, 0); // upper left
				glVertex3f(-halfWidth, -halfHeight, 0); // lower left
				glVertex3f(halfWidth, -halfHeight, 0); // lower right
				glEnd();
			}
			
			// Revert the cells position
			glPopMatrix();
		}
		
	}
	
	// Restore the modelview matrix
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	glMatrixMode(saveMode);
	
	// Check for OpenGL errors and log them if there are errors
	if(error = glGetError())
		[context logMessage:@"OpenGL error %04X", error];
	
	
	return (error ? NO : YES);

}

- (void) disableExecution:(id<QCPlugInContext>)context
{
	/*
	Called by Quartz Composer when the plug-in instance stops being used by Quartz Composer.
	*/
}

- (void) stopExecution:(id<QCPlugInContext>)context
{
	/*
	Called by Quartz Composer when rendering of the composition stops: perform any required cleanup for the plug-in.
	*/
}

@end
